package com.zzyl.nursing.service.impl;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.dto.QueryDeviceDataDto;
import com.zzyl.nursing.job.vo.IotMsgNotifyData;
import com.zzyl.nursing.job.vo.IotMsgService;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.util.DateTimeZoneConverter;
import com.zzyl.nursing.vo.QueryDeviceDateVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceDataMapper;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.service.IDeviceDataService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.Arrays;

/**
 * 设备数据Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-04-29
 */
@Service
@Slf4j
public class DeviceDataServiceImpl extends ServiceImpl<DeviceDataMapper,DeviceData> implements IDeviceDataService
{
    @Autowired
    private DeviceDataMapper deviceDataMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 批量保存
     *
     * @param data
     */
    @Override
    public void batchInsertDeviceData(IotMsgNotifyData data) {
        //根据iotId查询设备'
        Device device = deviceMapper.selectOne(Wrappers.<Device>lambdaQuery().eq(Device::getIotId, data.getHeader().getDeviceId()));
        if(ObjectUtil.isEmpty(device)){
            log.info("设备不存在，设备id：{}",data.getHeader().getDeviceId());
            return;
        }

        //构建设备数据，可能会有多条
        List<IotMsgService> services = data.getBody().getServices();
        if(CollUtil.isEmpty(services)){
            log.info("设备数据为空");
            return;
        }
        //循序services
        services.forEach(s->{

            //处理上报时间
            String eventTimeStr = s.getEventTime();
            LocalDateTime localDateTime = LocalDateTimeUtil.parse(eventTimeStr, "yyyyMMdd'T'HHmmss'Z'");
            LocalDateTime eventTime = DateTimeZoneConverter.utcToShanghai(localDateTime);

            List<DeviceData> list = new ArrayList<>();

            s.getProperties().forEach((k,v)->{
                //属性拷贝，从设备拷贝到设备数据
                DeviceData deviceData = BeanUtil.toBean(device, DeviceData.class);
                deviceData.setId(null);
                deviceData.setCreateTime(null);
                deviceData.setFunctionId(k);
                deviceData.setDataValue(v+"");
                deviceData.setAlarmTime(eventTime);
                deviceData.setAccessLocation(device.getBindingLocation());
                list.add(deviceData);
            });
            //批量保存
            saveBatch(list);
            //保存到redis中
            redisTemplate.opsForHash().put(CacheConstants.IOT_DEVICE_LAST_DATA,device.getIotId(), JSONUtil.toJsonStr(list));
        });
    }

    /**
     * 查询设备数据
     * 
     * @param id 设备数据主键
     * @return 设备数据
     */
    @Override
    public DeviceData selectDeviceDataById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询设备数据列表
     * 
     * @param deviceData 设备数据
     * @return 设备数据
     */
    @Override
    public List<DeviceData> selectDeviceDataList(DeviceData deviceData)
    {
        return deviceDataMapper.selectDeviceDataList(deviceData);
    }

    /**
     * 新增设备数据
     * 
     * @param deviceData 设备数据
     * @return 结果
     */
    @Override
    public int insertDeviceData(DeviceData deviceData)
    {
        return save(deviceData)?1:0;
    }

    /**
     * 修改设备数据
     * 
     * @param deviceData 设备数据
     * @return 结果
     */
    @Override
    public int updateDeviceData(DeviceData deviceData)
    {
        return updateById(deviceData)?1:0;
    }

    /**
     * 批量删除设备数据
     * 
     * @param ids 需要删除的设备数据主键
     * @return 结果
     */
    @Override
    public int deleteDeviceDataByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids))?1:0;
    }

    /**
     * 删除设备数据信息
     * 
     * @param id 设备数据主键
     * @return 结果
     */
    @Override
    public int deleteDeviceDataById(Long id)
    {
        return removeById(id)?1:0;
    }

    /**
     * 按时间段查询设备数据列表
     * @param queryDeviceDataDto
     * @return
     */
    @Override
    public List<QueryDeviceDateVo> queryDeviceDataListByDay(QueryDeviceDataDto queryDeviceDataDto) {
        //1. 获取查询时间段的开始时间和结束时间
        // 将毫秒时间戳转换为LocalDateTime对象
        Long startMillis = Long.valueOf(queryDeviceDataDto.getStartTime());
        Long endMillis = Long.valueOf(queryDeviceDataDto.getEndTime());

        LocalDateTime startTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(startMillis), ZoneId.systemDefault());
        LocalDateTime endTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(endMillis), ZoneId.systemDefault());
        // 生成时间段内要查询的所有时间整点的集合
        List<LocalDateTime> hourPoints = generateHourPoints(startTime, endTime);

        //2. 查询时段内所有的数据
        List<DeviceData> rawData = deviceDataMapper.selectList(new QueryWrapper<DeviceData>()
                .eq("iot_id", queryDeviceDataDto.getIotId())
                .eq("function_id", queryDeviceDataDto.getFunctionId())
                .between("alarm_time",
                        startTime.minusMinutes(30), // 扩大查询范围确保覆盖整点前后
                        endTime.plusMinutes(30))
                .orderByAsc("alarm_time"));

        //3. 将所有的整点时间段都加入条件查询
        List<QueryDeviceDateVo> finalResult = new ArrayList<>();
        hourPoints.forEach(hourPoint -> {
            QueryDeviceDateVo queryDeviceDateVo = new QueryDeviceDateVo();
            queryDeviceDateVo.setDateTime(String.valueOf(hourPoint));
            finalResult.add(queryDeviceDateVo);
        });

        //4. 为每个整点找到最近的数据点
        List<QueryDeviceDateVo> result = generateHourPointData(rawData, finalResult);


        //5. 如果整点没有设备数据，则设置为0
        result.forEach(vo -> {
//          设备数据时间dateTime格式化为HH：mm 的格式
            LocalDateTime dateTime = LocalDateTime.parse(vo.getDateTime());
            vo.setDateTime(dateTime.format(DateTimeFormatter.ofPattern("HH:mm")));
//            将数据时间转换为整点时间
//            String dateTime = vo.getDateTime();
//            vo.setDateTime(dateTime.substring(11, 16));
            if (vo.getDateValue() == null) {
                vo.setDateValue(0.0);
            }
        });


        return result;
    }

    /**
     * 生成整点小时数据
     * @param rawData
     * @param result
     * @return
     */
    private List<QueryDeviceDateVo> generateHourPointData(List<DeviceData> rawData, List<QueryDeviceDateVo> result) {
        for (DeviceData data : rawData) {
            LocalDateTime recordTime = data.getAlarmTime();

            // 遍历需要的整点时间
            for (QueryDeviceDateVo vo : result) {
                LocalDateTime hourPoint = LocalDateTime.parse(vo.getDateTime());

                // 计算整点时间与上报时间的时间差
                long diffMillis = Math.abs(Duration.between(recordTime, hourPoint).toMillis());

                // 只考虑整点前后15分钟内的数据
                if (diffMillis <= Duration.ofMinutes(15).toMillis()) {
                    // 如果还没有数据，或者找到更接近的数据
                    if (vo.getDateValue() == null || diffMillis < Math.abs(Duration.between(
                            vo.getHourPoint(), hourPoint).toMillis())) {
                        vo.setDateValue(Double.valueOf(data.getDataValue()));
                        vo.setHourPoint(recordTime); // 存储原始时间
                    }
                }
            }
        }
        return result;
    }

    // 生成指定时间范围内的所有整点
    private List<LocalDateTime> generateHourPoints(LocalDateTime start, LocalDateTime end) {
        List<LocalDateTime> points = new ArrayList<>();

        // 参数校验
        if (start == null || end == null || start.isAfter(end)) {
            return points;
        }

        // 找到第一个整点（向上取整）
        LocalDateTime firstHour = start.truncatedTo(ChronoUnit.HOURS);
        if (start.isAfter(firstHour)) {
            firstHour = firstHour.plusHours(1);
        }

        // 找到最后一个整点（向下取整）
        LocalDateTime lastHour = end.truncatedTo(ChronoUnit.HOURS);

        // 如果最后一个整点早于结束时间，则包含该整点
        if (lastHour.isBefore(end)) {
            lastHour = lastHour;
        } else if (lastHour.isAfter(end)) {
            // 如果最后一个整点晚于结束时间，则取前一个整点
            lastHour = lastHour.minusHours(1);
        }

        // 生成所有整点
        LocalDateTime current = firstHour;
        while (!current.isAfter(lastHour)) {
            points.add(current);
            current = current.plusHours(1);
        }

        return points;
    }

    /**
     * 按时间段查询设备数据列表
     * @param queryDeviceDataDto
     * @return
     */
    @Override
    public List<QueryDeviceDateVo> queryDeviceDataListByWeek(QueryDeviceDataDto queryDeviceDataDto) {
        return List.of();
    }
}
